package org.ctsi.rsds.base.service;

import java.lang.reflect.Method;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.ctsi.rsds.base.dao.BasePOJO;
import org.ctsi.rsds.base.dao.NeedCache;
import org.ctsi.rsds.base.dao.cache.redis.RedisClusterClient;
import org.ctsi.rsds.base.launcher.AppLauncher;
import org.ctsi.rsds.base.util.UUIDUtils;
import org.springframework.stereotype.Component;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;
import com.github.pagehelper.PageHelper;

@Component
@Aspect
public class Service4DBAop {
	
    private static RedisClusterClient rc;
	
	@Before("execution(* cn.com.ctsi.csdp.*.service.*Service.create*(..))")
    public void createBefore(JoinPoint jp) {  
		if(jp.getArgs()[0] instanceof BasePOJO){
			BasePOJO newObj = (BasePOJO)jp.getArgs()[0];
			if(newObj.getUuid()==null){
				newObj.setUuid(UUIDUtils.getUUID());
			}
			newObj.setCreated(new Date());			
		}
    }
	
	@Before("execution(* cn.com.ctsi.csdp.*.service.*Service.find*(..))")
    public void findBefore(JoinPoint jp) {
		if(jp.getArgs().length==0){
			return;
		}
		if(!Map.class.isInstance(jp.getArgs()[0])){
			return;
		}
		@SuppressWarnings("unchecked")
		Map<String,Object> clause = (Map<String, Object>) jp.getArgs()[0];
		int startPage = 1;
		int pageSize = 0;
		if(clause.containsKey("startPage") && clause.containsKey("pageSize")){
			if(String.class.isInstance(clause.get("startPage"))){
				startPage = Integer.parseInt((String)clause.get("startPage"));
				pageSize = Integer.parseInt((String)clause.get("pageSize"));					
			}else{
				startPage = ((Integer)clause.get("startPage")).intValue();
				pageSize = ((Integer)clause.get("pageSize")).intValue();				
			}
		}
		PageHelper.startPage(startPage, pageSize, true);		
    }
	
	private RedisClusterClient getClient(){
		if(rc==null){
			if(AppLauncher.context!=null){
				Object o = AppLauncher.context.getBean("redisClusterClient");
				if(o!=null){
					rc = (RedisClusterClient)o;
				}				
			}
		}
		return rc;
	}
	
	private String parseId(Object[] args, String keyName){
		String id = null;
		//因为只支持针对一个实体对象进行缓存，keyName只标明从参数中如何取到那个实体的id
		//具体格式为：param.第几个参数.参数属性名
		String[] keys = keyName.split("\\.");
		if(keys.length==1){
			id = keys[0];
		}else if(keys.length>1){
			int paramPos = Integer.parseInt(keys[1]);
			Object arg = args[paramPos];
			if(keys.length==2){
				id = arg.toString();	
			}else if(keys.length==3){
				JSONObject jsonObject = (JSONObject) JSONObject.toJSON(arg);
				id=jsonObject.getString(keys[2]);
			}
		}
		
		return id;
	}
	
	private String parseSubKey(Object[] args, String subKeyName){
		String id = "";
		String[] keys = subKeyName.split(";");
		for(String key : keys){
			id = id + ":" + parseId(args, key);
		}
		return id;
	}
	
	@Around("execution(* cn.com.ctsi.csdp.*.dao.*Mapper.*(..))")
	public Object daoMethodAround(ProceedingJoinPoint pjp) throws Throwable{
		Object ret= null;
		MethodSignature sign = (MethodSignature) pjp.getSignature();
		Method method = sign.getMethod();
		try{
			//缓存的数据格式为：(pojo类名:实体id)为主key，内容为map格式，map中方法名为key，方法结果为value
			if(method.isAnnotationPresent(NeedCache.class)){
				NeedCache cacheInfo = method.getAnnotation(NeedCache.class);
				String key = cacheInfo.entityName() + ":" + parseId(pjp.getArgs(),cacheInfo.keyName());
				if(cacheInfo.type()==NeedCache.type.GET){
					String subKey = cacheInfo.subKey().equals("") ? sign.getDeclaringType().getSimpleName() + "." + method.getName() :
						parseSubKey(pjp.getArgs(),cacheInfo.subKey());
					String json = getClient().getHashValue(key, subKey).get(subKey);
					if(json != null){
			        	try{
			        		Class<?> retType = method.getReturnType();
			        		ret = JSON.parseObject(json, retType);
			        		return ret;
			        	}catch(JSONException e){//如果缓存中的数据格式有误，清除缓存
			        		getClient().deleteHashField(key,subKey);
						}
					}
					//执行数据库操作
					ret = pjp.proceed(pjp.getArgs());
					
					//缓存的一种实现是把javabean转成json字符串进行缓存
					Map<String,String> data = new HashMap<String,String>();
					data.put(subKey, JSON.toJSONString(ret));
					getClient().setHash(key, data);	
				}else{
					ret = pjp.proceed(pjp.getArgs());
					if(cacheInfo.type()==NeedCache.type.DELETE || cacheInfo.type()==NeedCache.type.UPDATE){
						getClient().delete(key);
					}else if(cacheInfo.type()==NeedCache.type.INSERT){
						//插入方法产生的数据暂时不考虑缓存，因为数据库中有字段可以设置默认值，这种情况下缓存，这类字段值无法缓存
					}
				}
			}else{//不需要缓存，按原方式执行
				ret = pjp.proceed(pjp.getArgs());
			}
		}catch(Exception e){
			throw e;
		} catch (Throwable e) {
			throw e;
		}
		return ret;
	}
}
